﻿using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.Devices.Usb;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System.Threading;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using 纸片人.AttributeUsageFloder;

// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace 纸片人.PageViews
{

    [LastModified("2022-5-12", "create MeshDeformationPage.xaml")]
    [PageType("cpu", "网格变形")]
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class MeshDeformationPage : Page
    {
        /// <summary>
        /// 方块数量
        /// </summary>
        int cube = 0;
        const int size = 37;
        int mod = 3;
        Point[,] points = new Point[size, size];

        Color[] source;
        Color[] tempColor;
        List<Color[]> listColor = new List<Color[]>();

        Thumb[,] listThumb = new Thumb[size, size];

        int width = 300;
        int height = 300;

        int partWidth = 0;
        int partHeight = 0;


        double pt, pr, pl, pb;

        /// <summary>
        /// 是否显示网格
        /// </summary>
        bool visibleGrid = true;

        public MeshDeformationPage()
        {
            this.InitializeComponent();


            AddThumb();

            canvas.Invalidate();
        }

        void AddThumb()
        {
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    Point point = new Point(i * width / (size - 1), height / (size - 1) * j);
                    points[i, j] = point;



                    if (i % mod != 0 && j % mod != 0)
                    {
                        continue;
                    }



                    if (i % mod != 0 || j % mod != 0)
                    {
                        //thumb.Background = new SolidColorBrush(Colors.Transparent);

                    }
                    else
                    {
                        Thumb thumb = new Thumb();

                        thumb.Background = new SolidColorBrush(Colors.Red);
                        thumb.DragDelta += Thumb_DragDelta;
                        thumb.Tag = i;
                        thumb.Name = j.ToString();
                        thumb.Width = thumb.Height = 10;
                        topCanvas.Children.Add(thumb);

                        listThumb[i, j] = thumb;
                    }





                }
            }
        }

        private void InitialPoints()
        {
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    Point point = new Point(i * width / (size - 1), height / (size - 1) * j);
                    points[i, j] = point;

                }
            }
        }

        void Initial()
        {






            for (int i = 0; i < size; i++)
            {



                for (int j = 0; j < size; j++)
                {

                    if (i % mod != 0 && j % mod != 0)
                    {
                        continue;
                    }
                    Point point = points[i, j];

                    Thumb thumb = listThumb[i, j];

                    SetThumbPoint(thumb, point);



                }
            }

        }


        void SetThumbPoint(Thumb thumb, Point point)
        {
            if (thumb == null)
                return;

            double mt = (topCanvas.ActualHeight - height) / 2;
            double ml = (topCanvas.ActualWidth - width) / 2;
            thumb.Translation = new Vector3(point.ToVector2() - new Vector2(5, 5) + new Vector2((float)ml, (float)mt), 0);

        }

        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Thumb t = sender as Thumb;

            float oh = (float)e.HorizontalChange;
            float ov = (float)e.VerticalChange;

            int ii = (int)t.Tag;
            int jj = int.Parse(t.Name);

            var p = points[ii, jj];
            p.X = p.X + oh;
            p.Y = p.Y + ov;
            points[ii, jj] = p;

            SetThumbPoint(t, p);


            jisuanltrb(ii, jj, oh, ov);

            jisuanY(ii, jj, oh, ov, true);
            jisuanY(ii, jj, oh, ov, false);

            jisuanX(ii, jj, oh, ov, true);
            jisuanX(ii, jj, oh, ov, false);

            canvas.Invalidate();
        }

        void jisuanY(int i, int j, float ox, float oy, bool upOrDown)
        {
            int cj = 0;

            if (upOrDown)
            {
                cj = j - mod;



            }
            else
            {
                cj = j + mod;
            }

            //if (cj == 0 || cj == size - 1)
            //{
            //    jisuanltrb(i, cj, ox / 2, oy / 2);

            //}

            if (cj <= 0 || cj >= size - 1)
            {

                return;
            }

            var p = points[i, cj];
            p.X = p.X + ox / 2;
            p.Y = p.Y + oy / 2;
            points[i, cj] = p;

            SetThumbPoint(listThumb[i, cj], p);

            jisuanltrb(i, cj, ox / 2, oy / 2);

            jisuanY(i, cj, ox / 2, oy / 2, upOrDown);
            jisuanX(i, cj, ox / 2, oy / 2, upOrDown);
            jisuanX(i, cj, ox / 2, oy / 2, !upOrDown);

        }


        void jisuanX(int i, int j, float ox, float oy, bool upOrDown)
        {
            int ci = 0;

            if (upOrDown)
            {
                ci = i - mod;



            }
            else
            {
                ci = i + mod;
            }

            //if (ci == 0 || ci == size - 1)
            //{
            //    jisuanltrb(ci, j, ox / 2, oy / 2);

            //}

            if (ci <= 0 || ci >= size - 1)
            {
                return;
            }

            var p = points[ci, j];
            p.X = p.X + ox / 2;
            p.Y = p.Y + oy / 2;
            points[ci, j] = p;

            SetThumbPoint(listThumb[ci, j], p);
            jisuanltrb(ci, j, ox / 2, oy / 2);

            jisuanX(ci, j, ox / 2, oy / 2, upOrDown);

        }


        void jisuanltrb(int i, int j, float ox, float oy)
        {
            int ty = 0, by = 0, lx = 0, rx = 0;

            ty = j - 1;
            by = j + 1;
            lx = i - 1;
            rx = i + 1;

            if (j == 0)
            {
                int count = 1;
            }

            if (i == 0 && j == 0)
            {
                jusan(rx, j, ox, oy);
                jusan(i, by, ox, oy);
            }
            else if (i == size - 1 && j == 0)
            {
                jusan(lx, j, ox, oy);
                jusan(i, by, ox, oy);

            }
            else if (i == 0 && j == size - 1)
            {
                jusan(i, ty, ox, oy);
                jusan(rx, j, ox, oy);
            }
            else if (i == size - 1 && j == size - 1)
            {
                jusan(lx, j, ox, oy);
                jusan(i, ty, ox, oy);
            }
            else if (i == 0)
            {
                jusan(i, ty, ox, oy);
                jusan(i, by, ox, oy);
                jusan(rx, j, ox, oy);
            }
            else if (i == size - 1)
            {
                jusan(i, ty, ox, oy);
                jusan(i, by, ox, oy);
                jusan(lx, j, ox, oy);
            }
            else if (j == 0)
            {
                jusan(lx, j, ox, oy);
                jusan(rx, j, ox, oy);
                jusan(i, by, ox, oy);
            }
            else if (j == size - 1)
            {
                jusan(lx, j, ox, oy);
                jusan(rx, j, ox, oy);
                jusan(i, ty, ox, oy);
            }
            else
            {
                jusan(i, ty, ox, oy);
                jusan(i, by, ox, oy);
                jusan(lx, j, ox, oy);
                jusan(rx, j, ox, oy);
            }


        }

        void jusan(int i, int j, float ox, float oy)
        {
            Point p = points[i, j];

            p.X = p.X + ox;
            p.Y = p.Y + oy;
            points[i, j] = p;

            SetThumbPoint(listThumb[i, j], p);

        }

        private void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            float mt = (float)(topCanvas.ActualHeight - height) / 2f;
            float ml = (float)(topCanvas.ActualWidth - width) / 2f;

            Vector2 margin = new Vector2(ml, mt);

            using (var ds = args.DrawingSession)
            {
                args.DrawingSession.Clear(Colors.Gray);




                // 画辅助先
                Function.DrawGridGraphics(ds, 50);





                if (source != null)
                {

                    CanvasRenderTarget crt = new CanvasRenderTarget(canvas, (float)canvas.ActualWidth, (float)canvas.ActualHeight, 96f);

                    using (var cds = crt.CreateDrawingSession())
                    {


                        pt = 9999999;
                        pb = 0;
                        pr = 0;
                        pl = 9999999;


                        for (int i = 0; i <= cube - 1; i++)
                        {
                            int x = i;
                            List<IAsyncAction> listTask = new List<IAsyncAction>();

                            for (int j = 0; j <= cube - 1; j++)
                            {
                                int y = j;

                                listTask.Add(ThreadPool.RunAsync(P =>
                                {
                                    try
                                    {

                                    
                                    CreateCanvasGeometry(x * 3, y * 3, listColor[y + x * cube], margin, cds);
                                    }
                                    catch
                                    {
                                        int count = x + y;
                                    }


                                }));
                            }

                              Task.WaitAll(listTask.Select(q => q.AsTask()).ToArray());

                        }

                    }



                    if (visibleGrid)
                    {
                        Color[] cache = crt.GetPixelColors();

                        foreach (var item in cache)
                        {
                            if (item.A != 0)
                            {

                            }
                        }

                        ds.DrawImage(crt);


                    }
                    else
                    {
                        int dw = (int)(pr - pl);
                        int dh = (int)(pb - pt);

                        Color[] cache = crt.GetPixelColors();



                        var cache1 = Function.RencentFillColor(cache, canvas.ActualWidth, canvas.ActualHeight);


                        ds.DrawImage(CanvasBitmap.CreateFromColors(canvas, cache1, (int)canvas.ActualWidth, (int)canvas.ActualHeight));

                    }


                }

                if (visibleGrid)
                {
                    for (int i = 0; i < size; i++)
                    {
                        if (i % mod != 0)
                        {
                            continue;
                        }

                        CanvasPathBuilder path = new CanvasPathBuilder(sender);

                        Point start = points[i, 0];



                        Vector2 sv = start.ToVector2() + new Vector2(ml, mt);

                        Vector2 st = sv;

                        path.BeginFigure(sv);
                        for (int j = 1; j < size; j++)
                        {
                            if (j > size - 1)
                            {
                                break;
                            }



                            Vector2 point = points[i, j].ToVector2() + margin;
                            Vector2 p1 = points[i, ++j].ToVector2() + margin;
                            Vector2 p2 = points[i, ++j].ToVector2() + margin;



                            path.AddCubicBezier(point, p1, p2);



                        }

                        path.EndFigure(CanvasFigureLoop.Open);

                        CanvasGeometry gemetry = CanvasGeometry.CreatePath(path);

                        ds.DrawGeometry(gemetry, Colors.Gray);

                    }


                    for (int j = 0; j < size; j++)
                    {
                        if (j % mod != 0)
                        {
                            continue;
                        }
                        Point start = points[0, j];

                        Vector2 sv = start.ToVector2() + new Vector2(ml, mt);


                        CanvasPathBuilder path = new CanvasPathBuilder(sender);

                        path.BeginFigure(sv);
                        for (int i = 1; i < size; i++)
                        {
                            if (i > size - 1)
                            {
                                break;
                            }



                            Vector2 point = points[i, j].ToVector2() + margin;
                            Vector2 p1 = points[++i, j].ToVector2() + margin;
                            Vector2 p2 = points[++i, j].ToVector2() + margin;



                            path.AddCubicBezier(point, p1, p2);



                        }

                        path.EndFigure(CanvasFigureLoop.Open);

                        CanvasGeometry gemetry = CanvasGeometry.CreatePath(path);

                        ds.DrawGeometry(gemetry, Colors.Gray);

                    }
                }






            }
        }


        void CreateCanvasGeometry(int x, int y, Color[] sour, Vector2 margin, Microsoft.Graphics.Canvas.CanvasDrawingSession ds)
        {
            CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
            builder.BeginFigure(points[x, y].ToVector2() + margin);


            Side tSide, lSide, rSide, bSide;

            tSide = new Side();
            lSide = new Side();
            rSide = new Side();
            bSide = new Side();

            var tds = new DeformSide(points[x, y], points[x + 1, y], points[x + 2, y], points[x + 3, y]);

            var rds = new DeformSide(points[x + 3, y], points[x + 3, y + 1], points[x + 3, y + 2], points[x + 3, y + 3]);
            var bds = new DeformSide(points[x, y + 3], points[x + 1, y + 3], points[x + 2, y + 3], points[x + 3, y + 3]);
            var lds = new DeformSide(points[x, y], points[x, y + 1], points[x, y + 2], points[x, y + 3]);


            tSide.BesselList.Add(tds);
            tSide.Length = tds.GetBezierLength();

            rSide.BesselList.Add(rds);
            rSide.Length = rds.GetBezierLength();

            bSide.BesselList.Add(bds);
            bSide.Length = bds.GetBezierLength();

            lSide.BesselList.Add(lds);
            lSide.Length = lds.GetBezierLength();

            double sideWidth = Math.Max(tSide.Length, bSide.Length);
            double sideHeight = Math.Max(lSide.Length, rSide.Length);


            Point[,] horizontalMatric = new Point[(int)sideWidth, 2];
            Point[,] verticalMatric = new Point[(int)sideHeight, 2];

            double t = 9999999;
            double b = 0;
            double r = 0;
            double l = 9999999;

            for (double xx = 0; xx < (int)sideWidth; xx++)
            {


                double xm = xx / sideWidth;

                double ctx = tSide.Length * xm;
                foreach (var item in tSide.BesselList)
                {
                    if (item.BesselLength > ctx)
                    {

                        Point point = item.CalculateBezierPointForCubic(ctx / item.BesselLength);
                        if (point.Y < t)
                        {
                            t = point.Y;
                        }
                        else if (point.Y > b)
                        {
                            b = point.Y;
                        }

                        if (point.X < l)
                        {
                            l = point.X;
                        }
                        else if (point.X > r)
                        {
                            r = point.X;
                        }


                        horizontalMatric[(int)xx, 0] = point;

                        break;
                    }
                    else
                    {
                        ctx -= item.BesselLength;
                    }
                }

                double cbx = bSide.Length * xm;
                foreach (var item in bSide.BesselList)
                {
                    if (item.BesselLength > cbx)
                    {
                        Point point = item.CalculateBezierPointForCubic(cbx / item.BesselLength);

                        if (point.Y < t)
                        {
                            t = point.Y;
                        }
                        else if (point.Y > b)
                        {
                            b = point.Y;
                        }

                        if (point.X < l)
                        {
                            l = point.X;
                        }
                        else if (point.X > r)
                        {
                            r = point.X;
                        }
                        horizontalMatric[(int)xx, 1] = point;
                        break;
                    }
                    else
                    {
                        cbx -= item.BesselLength;
                    }
                }




            }
            for (double yy = 0; yy < (int)sideHeight; yy++)
            {


                double ym = yy / sideHeight;

                double cly = lSide.Length * ym;
                foreach (var item in lSide.BesselList)
                {
                    if (item.BesselLength > cly)
                    {

                        Point point = item.CalculateBezierPointForCubic(cly / item.BesselLength);

                        if (point.Y < t)
                        {
                            t = point.Y;
                        }
                        else if (point.Y > b)
                        {
                            b = point.Y;
                        }

                        if (point.X < l)
                        {
                            l = point.X;
                        }
                        else if (point.X > r)
                        {
                            r = point.X;
                        }

                        verticalMatric[(int)yy, 0] = point;

                        break;
                    }
                    else
                    {
                        cly -= item.BesselLength;
                    }
                }

                double cry = rSide.Length * ym;
                foreach (var item in rSide.BesselList)
                {
                    if (item.BesselLength > cry)
                    {
                        Point point = item.CalculateBezierPointForCubic(cry / item.BesselLength);

                        if (point.Y < t)
                        {
                            t = point.Y;
                        }
                        else if (point.Y > b)
                        {
                            b = point.Y;
                        }

                        if (point.X < l)
                        {
                            l = point.X;
                        }
                        else if (point.X > r)
                        {
                            r = point.X;
                        }
                        verticalMatric[(int)yy, 1] = point;


                        break;
                    }
                    else
                    {
                        cry -= item.BesselLength;
                    }
                }




            }

            //图形的长和宽
            int shapew = (int)Math.Round(r - l);
            int shapeh = (int)Math.Round((b - t));

            pt = t < pt ? t : pt;
            pb = b > pb ? b : pb;
            pr = r > pr ? r : pr;
            pl = l < pl ? l : pl;

            var tempColors = new Color[shapew * shapeh];

            Point ltPoint = horizontalMatric[0, 0];
            Point rtPoint = horizontalMatric[(int)sideWidth - 1, 0];

            Point lbPoint = horizontalMatric[0, 1];
            Point rbPoint = horizontalMatric[(int)sideWidth - 1, 1];


            for (int xx = 0; xx < (int)sideWidth; xx++)
            {
                double mw = (double)xx / sideWidth;

                Point txy = horizontalMatric[xx, 0];
                Point bxy = horizontalMatric[xx, 1];

                for (int yy = 0; yy < (int)sideHeight; yy++)
                {
                    double mh = (double)yy / (sideHeight);

                    Point lxy = verticalMatric[yy, 0];
                    Point rxy = verticalMatric[yy, 1];

                    double oy = 0, ox = 0;

                    oy = lxy.Y - (lxy.Y - rxy.Y) * mw;
                    ox = txy.X - (txy.X - bxy.X) * mh;

                    double ll = (lxy.X - (ltPoint.X - (ltPoint.X - lbPoint.X) * mh)) * (1 - mw);
                    double rr = (rxy.X - (rtPoint.X - (rtPoint.X - rbPoint.X) * mh)) * mw;
                    ox = ox + ll + rr;
                    ox = ox - l;
                    double tt = (txy.Y - (ltPoint.Y - (ltPoint.Y - rtPoint.Y) * mw)) * (1 - mh);
                    double dd = (bxy.Y - (lbPoint.Y - (lbPoint.Y - rbPoint.Y) * mw)) * (mh);
                    oy = oy + tt + dd;

                    oy = oy - t;

                    if (ox < 0 || ox > (int)shapew - 1 || oy < 0 || oy > (int)shapeh - 1)
                        continue;

                    try
                    {
                        //Color c = Function.BiLinear(mw * partWidth, mh * partHeight, sour, (int)partWidth, (int)partHeight);
                        Color c = sour[(int)(mw * partWidth) + (int)(mh * partHeight) * partWidth];
                        tempColors[(int)ox + (int)oy * (int)shapew] = c;
                    }
                    catch
                    {
                        Color c = Function.BiLinear(mw * partWidth, mh * partHeight, sour, (int)partWidth, (int)partHeight);
                        tempColors[(int)ox + (int)oy * (int)shapew] = c;
                    }


                }
            }

            tempColors = Function.RencentFillColor(tempColors, shapew, shapeh);
            tempColors = Function.RencentFillColor(tempColors, shapew, shapeh);

            CanvasBitmap bitmap = CanvasBitmap.CreateFromColors(canvas, tempColors, shapew, shapeh);


            ds.DrawImage(bitmap, (float)(margin.X + ltPoint.X - (ltPoint.X - l)), (float)(margin.Y + ltPoint.Y - (ltPoint.Y - t)));




        }

        private void topCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            Initial();
        }

        private void topCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Initial();
        }



        private async void select_Click(object sender, RoutedEventArgs e)
        {
            CanvasBitmap bitmap = await Function.SelectImageAsync();
            WriteableBitmap wb;
            Image image = new Image();
            Function.InitialParameter(bitmap, out width, out height, out source, out tempColor, image, out wb);
            InitialPoints();
            Initial();

            cube = size / 3;

            partWidth = (width) / cube;
            partHeight = (height) / cube;

            listColor.Clear();

            for (int i = 0; i < cube; i++)
            {
                for (int j = 0; j < cube; j++)
                {
                    Color[] c = CopyPart(bitmap, partWidth, partHeight, i * partWidth, j * partHeight);
                    listColor.Add(c);
                }
            }



            canvas.Invalidate();
        }

        private Color[] CopyPart(CanvasBitmap bitmap, int mw, int mh, int ox, int oy)
        {



            CanvasRenderTarget crt = new CanvasRenderTarget(bitmap.Device, mw, mh, 96f);
            using (var ds = crt.CreateDrawingSession())
            {
                ds.DrawImage(bitmap, -ox, -oy);
            }

            Color[] cc = crt.GetPixelColors();



            return cc;

        }

        private void visitilGrid_Checked(object sender, RoutedEventArgs e)
        {
            visibleGrid = (bool)visitilGrid.IsChecked;
            visitilGrid.Content = visibleGrid ? "隐藏网格" : "显示网格";

            topCanvas.Visibility = visibleGrid ? Visibility.Visible : Visibility.Collapsed;

            canvas.Invalidate();

        }

        private void visitilGrid_Unchecked(object sender, RoutedEventArgs e)
        {
            visibleGrid = (bool)visitilGrid.IsChecked;
            visitilGrid.Content = visibleGrid ? "隐藏网格" : "显示网格";

            topCanvas.Visibility = visibleGrid ? Visibility.Visible : Visibility.Collapsed;

            canvas.Invalidate();
        }
    }
}
